package dispatcher

import (
	"context"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"strings"
	"sync"
	"time"

	"dev.ixmxm.top/rms/boot"
	"dev.ixmxm.top/rms/codes"
	"dev.ixmxm.top/rms/config"
	"dev.ixmxm.top/rms/managers"
	"dev.ixmxm.top/rms/managers/chargemanager"
	"dev.ixmxm.top/rms/managers/devicemanager"
	"dev.ixmxm.top/rms/managers/locationmanager"
	"dev.ixmxm.top/rms/managers/logmanager"
	resourecemanager "dev.ixmxm.top/rms/managers/resourcemanager"
	"dev.ixmxm.top/rms/managers/robotmanager"
	"dev.ixmxm.top/rms/managers/taskmanager"
	"dev.ixmxm.top/rms/managers/templatemanager"
	"dev.ixmxm.top/rms/managers/trafficmanager"
	"dev.ixmxm.top/rms/managers/usermanager"
	"dev.ixmxm.top/rms/model"
	"dev.ixmxm.top/rms/proto"
	"dev.ixmxm.top/rms/utils/base64"
	"dev.ixmxm.top/rms/utils/json"
	"dev.ixmxm.top/rms/utils/log"
	"dev.ixmxm.top/rms/utils/tools"
	"go.mongodb.org/mongo-driver/bson"
	"golang.org/x/net/websocket"
	"google.golang.org/grpc"
	"google.golang.org/grpc/keepalive"
)

var GlobalManager *DispatchManager

//初始化模块对象
func New(db *boot.MongoDB) *DispatchManager {
	m := &DispatchManager{callers: make(map[string]*robotCaller, 0)}
	robotmanager.SetReconnectCallBack(m.ReConnectInfoToAgv)
	GlobalManager = m
	m.db = db
	mc := &managers.ManagerCluster{}
	mc.UM = usermanager.New(db)
	mc.WMM = locationmanager.New(db)
	mc.TM = taskmanager.NewTaskManager(db)
	mc.CM = chargemanager.New(db)
	mc.DM = devicemanager.New(db, mc.TM)
	mc.TRM = trafficmanager.NewTrafficManager(db, mc.DM, mc.TM, mc.WMM)
	//设置回调函数
	mc.WMM.SetInitLocationGroupCallBack(mc.TRM.InitLocationGroups)
	mc.LM = logmanager.NewLogManager(db)
	mc.TTM = templatemanager.NewTemplateManager(db)
	mc.RSM = resourecemanager.NewResourceManager(db)
	m.mc = mc
	systemCfg, err := robotmanager.GetConfigFromDb(context.Background())
	if err != nil || systemCfg == nil {
		panic("读取配置文件失败:" + err.Error())
	}
	m.SaveSystemConfigToCache(systemCfg)
	m.manualChargeList = make(map[string]uint8, 0)
	m.disChargeList = make(map[uint8]string, 0)
	m.projectList = make(map[string]model.SlamMap, 0)
	m.projectNameList = make(map[string]string, 0)
	m.normalTaskLock = make(map[string]*sync.Mutex, 0)
	m.normalTaskDoingFlag = make(map[string]*model.NomalTaskOccupy, 0)
	maps, errGetMaps := mc.TRM.GetAllMapFromDB(context.TODO())
	if errGetMaps == nil {
		for _, perMap := range maps {
			nomalTaskOccupy := new(model.NomalTaskOccupy)
			m.normalTaskDoingFlag[perMap.MapName] = nomalTaskOccupy
			m.normalTaskLock[perMap.MapName] = new(sync.Mutex)
		}
	}

	m.Init()
	return m
}

type DispatchManager struct {
	db                  *boot.MongoDB            //数据库上下文
	callerLock          sync.Mutex               //callerLocker
	callers             map[string]*robotCaller  //小车会话集合
	mc                  *managers.ManagerCluster //对象集合
	manualChargeLock    sync.Mutex               //充电指令锁
	manualChargeList    map[string]uint8         //充电指令集合
	genTaskLock         sync.Mutex               //任务锁
	normalTaskLock      map[string]*sync.Mutex   //普通任务锁
	normalTaskDoingFlag map[string]*model.NomalTaskOccupy
	disChargeLock       sync.Mutex               //断电指令锁
	disChargeList       map[uint8]string         //断电指令集合
	projectNameList     map[string]string        //工程名列表
	projectList         map[string]model.SlamMap //map_name-data 获得所有的工程地图
	SystemConfig        *model.SystemConfig      //系统配置(缓存),不可直接赋值保存，调用SaveSystemConfigToCache保存
}

func (m *DispatchManager) GetAllDisChargeList() map[uint8]string {
	return m.disChargeList
}
func (m *DispatchManager) GetAllChargeList() map[string]uint8 {
	return m.manualChargeList
}

func (m *DispatchManager) GetmanualChargeLock() *sync.Mutex {
	return &m.manualChargeLock
}

func (m *DispatchManager) GetNomalTaskOccupy(mapName string) *model.NomalTaskOccupy {
	return m.normalTaskDoingFlag[mapName]
}
func (m *DispatchManager) GetNormalTaskLock(mapName string) *sync.Mutex {
	return m.normalTaskLock[mapName]
}

func (m *DispatchManager) CallerLock() {
	m.callerLock.Lock()
}

func (m *DispatchManager) CallerUnLock() {
	m.callerLock.Unlock()
}

func (m *DispatchManager) IsBusy() bool {
	isBusy, err := m.mc.TM.IsBusy(context.Background())
	if err != nil {
		log.Errorf("check dispatch manager busy error:%s", err.Error())
		return true
	}
	return isBusy
}

//获取系统配置(缓存)
func (m *DispatchManager) GetSystemConfigFormCache() *model.SystemConfig {
	return m.SystemConfig
}

//保存系统配置(缓存)
func (m *DispatchManager) SaveSystemConfigToCache(config *model.SystemConfig) {
	m.SystemConfig = config
	m.mc.CM.SystemConfig = config
	m.mc.DM.SystemConfig = config
	m.mc.LM.SystemConfig = config
	m.mc.TM.SystemConfig = config
	m.mc.TRM.SystemConfig = config
	m.mc.TTM.SystemConfig = config
	m.mc.UM.SystemConfig = config
}

//agvid:所有充电规则
func (m *DispatchManager) GetAllEnableDayChargerRuleConfigToCache() map[string][]model.DayChargerRule {

	agvChargerRuleMap := make(map[string][]model.DayChargerRule, 0)
	if m.SystemConfig == nil || len(robotmanager.GetAllTimerChargerFromCache()) <= 0 {
		return agvChargerRuleMap
	}
	//归类
	for _, rule := range robotmanager.GetAllTimerChargerFromCache() {
		if len(rule.RobotIdList) <= 0 {
			continue
		}
		for _, agvId := range rule.RobotIdList {
			_, find := agvChargerRuleMap[agvId]
			if find {
				chargerRuleList := agvChargerRuleMap[agvId]
				chargerRuleList = append(chargerRuleList, rule)
				agvChargerRuleMap[agvId] = chargerRuleList
			} else {
				chargerRuleList := make([]model.DayChargerRule, 0)
				chargerRuleList = append(chargerRuleList, rule)
				agvChargerRuleMap[agvId] = chargerRuleList
			}
		}
	}
	return agvChargerRuleMap

}

//获得当前满足条件的日充电规则映射agv-满足条件的规则列表
func (m *DispatchManager) GetAllEnableDayChargerRuleMap(dealDayChargerRuleList []model.DayChargerRule) map[string]model.DayChargerRule {
	agvChargerRuleMap := make(map[string]model.DayChargerRule, 0)
	if len(dealDayChargerRuleList) <= 0 {
		return agvChargerRuleMap
	}
	//归类
	for _, rule := range dealDayChargerRuleList {
		if len(rule.RobotIdList) <= 0 {
			continue
		}
		for _, agvId := range rule.RobotIdList {
			_, find := agvChargerRuleMap[agvId]
			if find {
				//会存在一辆车对应上
				// oldRule := agvChargerRuleMap[agvId]
				//判断当前rule和oldRule之前的关系
				//判断当前规则和上个时间规则，谁最后时间越长，就取出谁
				if tools.CompareAGteB(rule.EndTime, agvChargerRuleMap[agvId].EndTime) {
					agvChargerRuleMap[agvId] = rule
				}

			} else {
				agvChargerRuleMap[agvId] = rule
			}
		}
	}

	return agvChargerRuleMap
}

//当前使能的日充电规则
func (m *DispatchManager) GetAllEnableDayChargerRules() []model.DayChargerRule {
	dayChargerRule := make([]model.DayChargerRule, 0)
	if m.SystemConfig == nil || len(robotmanager.GetAllTimerChargerFromCache()) <= 0 {
		return dayChargerRule
	}
	for _, rule := range robotmanager.GetAllTimerChargerFromCache() {
		if len(rule.RobotIdList) <= 0 {
			continue
		}
		//禁用这个规则
		if rule.IsEnable == 1 {
			continue
		}
		dayChargerRule = append(dayChargerRule, rule)

	}
	return dayChargerRule
}

func (m *DispatchManager) GetAllCallers() map[string]*robotCaller {
	return m.callers
}

func (m *DispatchManager) GetCaller(agvID string) (*robotCaller, bool) {
	m.CallerLock()
	defer m.CallerUnLock()
	cal, isOk := m.callers[agvID]
	return cal, isOk
}

func (m *DispatchManager) AddNewCaller(ctx context.Context, account *model.RobotAccount) {
	m.makeRobotCaller(ctx, account)
}
func (m *DispatchManager) EnableAgvCaller(ctx context.Context, agvID string) {
	robotsAccount, _ := robotmanager.GetAllAccountFromDB(ctx)
	for _, r := range robotsAccount {
		if r.AgvId == agvID {
			go m.makeRobotCaller(ctx, r)
			break
		}
	}
}

//添加充电命令
func (m *DispatchManager) AddManualCharge(agvID string, chargerID uint8) {
	//新增充电指令
	log.Warnf("新增充电指令：agv=%s chargerID=%d", agvID, chargerID)
	m.manualChargeLock.Lock()
	defer m.manualChargeLock.Unlock()
	m.manualChargeList[agvID] = chargerID
}

//添加断电命令
func (m *DispatchManager) AddDisCharge(agvID string, chargerID uint8) {
	//新增充电指令
	log.Warnf("新增断电指令：agv=%s chargerID=%d", agvID, chargerID)
	m.disChargeLock.Lock()
	defer m.disChargeLock.Unlock()
	if chargerID == 0 {
		return
	}
	m.disChargeList[chargerID] = agvID
}

//获取工程名列表
func (m *DispatchManager) GetProjectNameList() []string {
	//将projectList，转换成数组
	projec_name_list := make([]string, 0)
	for _, projec_name := range m.projectNameList {
		projec_name_list = append(projec_name_list, projec_name)
	}

	return projec_name_list
}

//获得所有的工程地图列表
func (m *DispatchManager) GetProjectMapList(project_id string) []model.SlamMap {
	//将projectList，转换成数组
	projec_map_list := make([]model.SlamMap, 0)
	for _, projec_map := range m.projectList {
		if project_id != "" {
			if projec_map.ProjectId == project_id {
				projec_map_list = append(projec_map_list, projec_map)
			}
		} else {
			projec_map_list = append(projec_map_list, projec_map)
		}
	}
	return projec_map_list
}

//获取任务对象
func (m *DispatchManager) TM() *taskmanager.TaskManager {
	return m.mc.TM
}

//交通管理对象
func (m *DispatchManager) TRM() *trafficmanager.TrafficManager {
	return m.mc.TRM
}

//库位管制对象
func (m *DispatchManager) WMM() *locationmanager.LocationManager {
	return m.mc.WMM
}

//资源管理对象
func (m *DispatchManager) RSM() *resourecemanager.ResourceManager {
	return m.mc.RSM
}

//充电桩对象
func (m *DispatchManager) CM() *chargemanager.ChargeManager {
	return m.mc.CM
}

//设备对象（电梯）
func (m *DispatchManager) DM() *devicemanager.DeviceManager {
	return m.mc.DM
}

//日志对象
func (m *DispatchManager) LM() *logmanager.LogManager {
	return m.mc.LM
}

//获取任务模板对象
func (m *DispatchManager) TTM() *templatemanager.TemplateManager {
	return m.mc.TTM
}

//获得用户管理对象
func (m *DispatchManager) UM() *usermanager.UserManager {
	return m.mc.UM
}

func (m *DispatchManager) Init() {
	startTime := time.Now()
	ctx := context.Background()
	fmt.Printf("rms config:%s\n", json.DumpString(config.Conf))
	//初始化任务ID和子任务ID索引
	errTaskIdInit := m.TaskIdInit(ctx)
	if errTaskIdInit != nil {
		log.Errorf("初始化任务ID和子任务ID索引失败 err:%s", errTaskIdInit.Error())
	}

	//初始化代价地图
	fmt.Printf("初始化代价地图中，可能需要较长时间......%dms\n", time.Now().UnixMilli()-startTime.UnixMilli())
	types, errGetTypes := robotmanager.GetAllTypesFromDB(ctx)
	if errGetTypes != nil {
		log.PanicfWithContext(ctx, "get all robot types error:%s", errGetTypes.Error())
	}
	m.mc.TRM.InitDijkstraMaps(types...)
	fmt.Printf("初始化代价地图完成 %dms\n", time.Now().UnixMilli()-startTime.UnixMilli())

	lis, err := net.Listen("tcp", ":8099")
	if err != nil {
		log.Error("failed to listen:", err.Error())
		return
	}
	max_Size := config.Conf.Grpc.MaxSize

	if max_Size == 0 {
		max_Size = 10
	}
	maxSize := max_Size * 1024 * 1024
	s := grpc.NewServer(
		grpc.MaxRecvMsgSize(maxSize), grpc.MaxSendMsgSize(maxSize),
		grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{PermitWithoutStream: true}),
		grpc.KeepaliveParams(keepalive.ServerParameters{
			MaxConnectionIdle: 3 * time.Second,
			Time:              5 * time.Second}),
		grpc.UnaryInterceptor(proto.CheckVersionGrpcInterceptor))
	proto.RegisterRMSServerServer(s, m)
	go func() {
		defer lis.Close()
		defer s.GracefulStop()
		if errListen := s.Serve(lis); errListen != nil {
			log.Error("start sim robot grpc service error:", errListen.Error())
		}
	}()
	//与所有机器人建立通讯连接
	robotsAccount, errGetRobots := robotmanager.GetAllAccountFromDB(ctx)
	if errGetRobots != nil {
		log.Error("get all robots error:", errGetRobots.Error())
		return
	}
	for _, r := range robotsAccount {
		robot := r
		if robot.IsEnable {
			go m.makeRobotCaller(ctx, robot)
		}
	}
	go m.processFailedPlan()
	go m.processIdleTask()
	go m.CheckRegisterTask()
	go m.CheckAssignMainTask()
	go m.CheckAssignTask()
	go m.CheckRunSubtask()
	go m.CheckRunningSubtask()
	go m.SubtaskFinishProcess()
	go m.TaskFinish()

	go m.genChargeTask()
	go m.checkRobotCharge()
	go m.checkFailedCharger()
	go m.genStandbyTask()
	go m.startAutoCleanDB(m.SystemConfig.LogCfg.AutoCleanDbInterval)
	go m.startReceiveRMSErrCode()
	go m.ReceiveDeviceWebsocket()
}
func (m *DispatchManager) ReceiveDeviceWebsocket() {
	readOriginMessage := func(ws *websocket.Conn) error {
	again:
		fr, err := ws.NewFrameReader()
		if err != nil {
			// log.Warnf("new frame reader err %v", err)
			return err
		}
		frame, err := ws.HandleFrame(fr)
		if err != nil {
			log.Warnf("handle frame err %v", err)
			return err
		}
		if frame == nil {
			goto again
		}

		bytes, err := ioutil.ReadAll(frame)
		if err != nil {
			log.Warnf("read frame data err %v", err)
		}
		reply := &struct {
			DoorInfos     []model.DoorInfo       `json:"door_status"`
			ElevatorInfos []model.ElevatorStatus `json:"elevator_status"`
		}{}
		if errBind := json.Bind(bytes, reply); errBind != nil {
			log.Warnf("bind http response body data error:%s", errBind.Error())
			return nil
		}
		m.DM().SetAllDeviceStatus(reply.DoorInfos, reply.ElevatorInfos)
		// log.Infof("%s", string(bytes))
		return nil
	}
	wsurl := fmt.Sprintf("ws://%s:%d/efork_websocket/device/get_devices_status", m.SystemConfig.MiddlewareCfg.Ip, m.SystemConfig.MiddlewareCfg.PortWebsocket)
	origin := fmt.Sprintf("http://%s:%d", m.SystemConfig.MiddlewareCfg.Ip, m.SystemConfig.MiddlewareCfg.PortWebsocket)
	ws, err := websocket.Dial(wsurl, "", origin)
	if err != nil {
		for {
			ws, err = websocket.Dial(wsurl, "", origin)
			if err == nil {
				break
			}
			tools.Sleep(1)
		}
	}
	startTime := time.Now()
	for {
		if time.Now().Sub(startTime) > 5*time.Second {
			if time.Now().Sub(startTime) < time.Duration(6)*time.Second {
				log.Warnf("ReceiveDeviceWebsocket 超时大于5秒，断线重连")
			}
			ws, err = websocket.Dial(wsurl, "", origin)
			if err == nil {
				log.Infof("ReceiveDeviceWebsocket 断线重连成功")
				startTime = time.Now()
			}
		} else {
			err := readOriginMessage(ws)
			if err == nil {
				startTime = time.Now()
			}
		}
		tools.Sleep(0.2)
	}
}

//初始化任务ID和子任务ID索引
func (m *DispatchManager) TaskIdInit(ctx context.Context) codes.Code {
	err := m.mc.TM.SubtaskIdInit(ctx)
	if err != nil {
		return err
	}
	err = m.mc.TM.TaskIdInit(ctx)
	if err != nil {
		return err
	}
	return nil
}

func (m *DispatchManager) GetProjectInfo(ip, port string) []model.SlamMap {
	url := fmt.Sprintf("http://%s:%s/api/sync/pull_sync_project", ip, port)
	contentType := "application/json"
	data := `{"project_id":"","source":"rms"}`
	resp, err := http.Post(url, contentType, strings.NewReader(data))
	if err != nil {
		fmt.Printf("err: %v\n", err)
		return []model.SlamMap{}
	}
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Printf("err: %v\n", err)
		return []model.SlamMap{}
	}
	reply := &struct {
		Code int `json:"result_code"`
		Data struct {
			Projects []model.SlamMap `json:"projects"`
		} `json:"data"`
		Message string `json:"result_msg"`
	}{}
	if errBind := json.Bind(b, reply); errBind != nil {
		log.Errorf("bind http response body data error:%s", errBind.Error())
		return []model.SlamMap{}
	}
	if reply.Code == 0 {
		// fmt.Println(string(b))
		return reply.Data.Projects
	}
	return []model.SlamMap{}
}

func (m *DispatchManager) GetAgvProjectList(agvId string) {
	robot := robotmanager.GetFromCache(agvId)
	if robot.Account.AgvIP == "127.0.0.1" {
		return
	}
	project_list := m.GetProjectInfo(robot.Account.AgvIP, "9002")
	if len(project_list) > 0 {
		for _, project := range project_list {
			m.projectNameList[project.ProjectId] = project.ProjectId
			m.projectList[project.ProjectId] = project
			for _, traffic_map := range project.TrafficList {
				map_name := m.mc.TRM.GetMapName(project.ProjectId, traffic_map.MapName)
				if map_name == "" {
					var RawBase64 base64.Data = base64.Data(traffic_map.MapData)
					mapData, mapXml, errParse := model.ParseMap(RawBase64.MustDecode())
					if errParse != nil {
						log.Errorf("解析xml地图出错 error:%s", errParse.Error())
						continue
					}
					if project.ProjectId != mapData.MapInfo.ProjectId || traffic_map.MapName != mapData.MapInfo.TrafficName {
						log.Errorf("工程%s或者地图名%s与xml内部key不一致", project.ProjectId, traffic_map.MapName)
						continue
					}
					if project.ProjectId == "" || project.ProjectId == "NULL" {
						log.Errorf("小车%s地图(%s)获取工程名称失败,不可为空", agvId, traffic_map.MapName)
						continue
					}
					map_data := &model.Map{
						MapName:           fmt.Sprintf("%s@%s", project.ProjectId, traffic_map.MapName),
						ProjectId:         project.ProjectId,
						TrafficName:       traffic_map.MapName,
						MapData:           *mapData,
						RawBase64:         RawBase64,
						MapDataJsonString: json.DumpString(mapXml),
					}
					log.Infof("新增地图%s", fmt.Sprintf("%s@%s", project.ProjectId, traffic_map.MapName))
					if errUpdate := m.TRM().SaveMapToDB(context.Background(), map_data); errUpdate != nil {
						continue
					}
				}
			}
		}
	}
}

func (m *DispatchManager) ReConnectInfoToAgv(robot *model.Robot) {
	if !robot.StatusIsExists() {
		return
	}
	//下发清除指令到AGV
	caller, _ := m.GetCaller(robot.Account.AgvId)
	if caller == nil {
		log.Infof("没有找到小车连接")
		return
	}
	err := caller.SendBlockConflictAlarm(context.TODO(), -1)
	if err != nil {
		return
	}
	//发送成功时间清0
	if robot.StatusIsExists() {
		robot.Status.LastCommTime = model.Now()
	}
}

//与机器人建立通讯连接
func (m *DispatchManager) makeRobotCaller(ctx context.Context, robotAccount *model.RobotAccount) {
	log.Infof("start connect robot:%s", robotAccount.AgvId)
	caller := newRobotCaller(*robotAccount, robotmanager.UpdateStatusToCache)
	// if errSend := caller.SendMaps(ctx, maps); errSend != nil {
	// 	log.Warnf("send maps to robot:%s error:%s", robotAccount.AgvId, errSend.Error())
	// }
	log.Infof("add caller:%s", robotAccount.AgvId)
	m.CallerLock()
	m.callers[robotAccount.AgvId] = caller
	m.CallerUnLock()

	go func() {
		if robotAccount.AgvIP == "127.0.0.1" {
			return
		}
		project_list := m.GetProjectInfo(robotAccount.AgvIP, "9002")
		if len(project_list) > 0 {
			for _, project := range project_list {
				m.projectNameList[project.ProjectId] = project.ProjectId
				cur, find := m.projectList[project.ProjectId]
				if !find {
					m.projectList[project.ProjectId] = project
				} else {
					if len(project.TrafficList) > len(cur.TrafficList) {
						m.projectList[project.ProjectId] = project
					}
				}
			}
		}
	}()
}

func (m *DispatchManager) processMapFailedPlan(ctx context.Context, mapName string) {
	for {
		records, errGet := m.mc.TRM.GetMapFailedPlanToDB(context.Background(), mapName)
		if errGet != nil {
			tools.Sleep(1)
			continue
		}
		//先给所有失败任务规划的AGV设置阻塞状态
		agvs := make([]string, 0)
		for _, record := range records {
			robot := robotmanager.GetFromCache(record.AgvID)
			//失败的任务设置阻塞
			if robot.StatusIsExists() {
				robot.Status.IsBlock = true
			}
			agvs = append(agvs, record.AgvID)
		}
		if len(agvs) > 0 {
			log.Infof("失败机器人列表%v", agvs)
		}
		for _, record := range records {
			robot := robotmanager.GetFromCache(record.AgvID)
			subtask, errGetTask := m.mc.TM.GetRobotStatusSubtaskFromDB(ctx, record.AgvID, []model.TaskStatus{model.TaskStatusRunning, model.TaskStatusInTheTransit, model.TaskStasusAvoid, model.TaskStatusPaused, model.TaskStatusContinuing})
			if errGetTask != nil && !errors.Is(errGetTask, codes.ErrTaskNotExists) {
				log.Errorf("robot:%s retry task plan failed,get task error:%s", record.AgvID, errGetTask.Error())
				continue
			}
			if errGetTask != nil && errors.Is(errGetTask, codes.ErrTaskNotExists) {
				log.Warnf("robot:%s retry task plan failed,robot task not exist:%s", record.AgvID, errGetTask.Error())
				m.mc.TRM.DeleteFailedPlanFromDB(ctx, record.Uuid, robot)
				continue
			}
			task, _ := m.mc.TM.GetTaskFromDB(ctx, subtask.TaskId)
			log.Infof("robot:%s retry RmsTask:%d, RetryCnt:%d, subtask:%d  plan once", record.AgvID, task.Id, task.RetryCnt, subtask.Id)
			if task.Type == model.TaskTypeAvoid {
				if task.RetryCnt > 3 {
					caller, _ := m.GetCaller(record.AgvID)
					if caller == nil {
						log.Infof("没有找到小车连接")
						continue
					}
					if !robot.StatusIsExists() {
						continue
					}
					if robot.Status.Status == model.RobotStatusIdle {
						ctxTx := boot.Begin(context.Background())
						err := m.mc.TM.UpdateTaskStatusToDB(ctxTx, task.Id, model.TaskStatusCanceled)
						if err != nil {
							log.Errorf("robot:%s 空闲避让任务:%d  打断因为空闲设置已取消出错!!!", record.AgvID, task.Id)
							ctxTx.Rollback()
							continue
						}
						//解除任务绑定
						if task.MainTaskCode == "" {
							log.Warnf("robot:%s DeleteMainTaskByAgvIdToDB", task.AgvId)
							err = m.TM().DeleteMainTaskByAgvIdToDB(ctxTx, task.AgvId)
							if err != nil {
								ctxTx.Rollback()
								log.Errorf("空闲避让任务完成失败,数据库解除任务绑定失败  taskid:%d", task.Id)
								continue
							}
						} else {
							err = m.TM().UpdateMainTaskToDB(ctxTx, task.MainTaskCode, task.Id, task.AgvId)
							if err != nil {
								ctxTx.Rollback()
								log.Errorf("空闲避让任务完成失败,数据库解除任务绑定失败  taskid:%d", task.Id)
								continue
							}
						}
						//删除对应子任务
						err = m.TM().DeleteSubtasksToDB(ctxTx, task.Id)
						if err != nil {
							ctxTx.Rollback()
							log.Errorf("空闲避让任务完成失败，数据库删除子任务失败  taskid:%d", task.Id)
							continue
						}
						//删除失败任务
						err = m.mc.TRM.DeleteFailedPlanFromDB(ctxTx, record.Uuid, robot)
						if err != nil {
							log.Errorf("robot:%s 空闲避让任务:%d 打断删除失败任务规划出错!!!", record.AgvID, task.Id)
							ctxTx.Rollback()
							continue
						}
						ctxTx.Commit()
						continue
					}
					//开启事务
					ctxTx := boot.Begin(context.Background())
					err1 := caller.OperateTask(ctxTx, subtask.Id, m.mc.TM, proto.TaskOperatePb_TaskCancel)
					if err1 != nil {
						log.Errorf("robot:%s 空闲避让任务:%d 打断因为设置已取消出错!!!", record.AgvID, task.Id)
						ctxTx.Rollback()
						continue
					}
					err1 = m.mc.TM.UpdateTaskStatusToDB(ctxTx, task.Id, model.TaskStatusCanceling)
					if err1 != nil {
						log.Errorf("robot:%s 空闲避让任务:%d 打断更新状态取消中出错!!!", record.AgvID, task.Id)
						ctxTx.Rollback()
						continue
					}
					err1 = m.mc.TRM.DeleteFailedPlanFromDB(ctxTx, record.Uuid, robot)
					if err1 != nil {
						log.Errorf("robot:%s 空闲避让任务:%d 打断删除失败任务规划出错!!!", record.AgvID, task.Id)
						ctxTx.Rollback()
						continue
					}
					ctxTx.Commit()
					continue
				} else {
					data := bson.M{"retry_cnt": task.RetryCnt + 1}
					m.db.Collection(ctx, model.Task{}.TableName()).FindOneAndUpdate(ctx, bson.M{"id": task.Id}, bson.M{"$set": data})
				}
			}
			if subtask.Status == model.TaskStatusPaused || subtask.Status == model.TaskStatusPausing {
				continue
			}
			if !robot.StatusIsExists() || robot.Status.Online == false || robot.Status.TaskOnline == false || robot.Status.Status == model.RobotStatusEmergenceStop {
				log.Warnf("robot:%s retry task plan failed: robot status is not ready ", record.AgvID)
				continue
			}
			if (subtask.SubStatus == model.WalkAvoidFinish || subtask.SubStatus == model.WalkTempFinish) && time.Now().Sub(subtask.BlockAvoidTime) < time.Duration(0.5*m.SystemConfig.TrafficCfg.TaskAvoidCfg.AvoidTriggerTime)*time.Second {
				continue
			}

			if subtask.RetryCount%15 == 0 {
				// log.Infof("robot:%s retry RmsTask:%d, subtask:%d  plan once", record.AgvID, subtask.TaskId, subtask.Id)
				subtask.RetryCount = 0
			}
			log.Info("processMapFailedPlan subtask.Id:%d  subtask.RetryCount:%d ", subtask.Id, subtask.RetryCount+1)
			m.mc.TM.UpdateSubtaskKeyIntToDB(context.Background(), subtask.Id, "retry_count", subtask.RetryCount+1)
			//任务优先级相同且互相堵塞，判断堵塞AGV为避让中，则当前AGV不触发避让
			avoidFlag := m.SystemConfig.TrafficCfg.UseTaskAvoid //true
			if subtask.BlockAgv != "" && subtask.Grade == subtask.BlockTaskGrade {
				runTask, _ := m.mc.TM.GetRunningSubtaskFromDB(ctx, subtask.BlockAgv)
				blockPath, getErr := m.mc.TRM.GetRobotLocalPathFromDB(ctx, subtask.BlockAgv)
				if getErr == nil {
					if runTask != nil && runTask.Status == model.TaskStasusAvoid && len(blockPath.Paths) > 0 {
						log.Infof("任务优先级相同，robot%s等待，robot%s避让", subtask.AgvId, subtask.BlockAgv)
						avoidFlag = false
					}
				}
			}
			if m.mc.TM.IsRobotTriggerIdleAvoidTask(ctx, subtask.AgvId) && m.SystemConfig.TrafficCfg.UseIdleAvoid {
				avoidFlag = false
			}
			if m.TRM().IsAgvInFailedPlanCycleWait(ctx, subtask.BlockAgv) {
				avoidFlag = false
			}
			err := m.RunSubtask(context.Background(), subtask, robot, true, avoidFlag)
			if err != nil {
				if subtask.RetryCount%15 == 0 {
					log.Warnf("robot:%s retry plan failed:%s", record.AgvID, err.Error())
				}
				m.mc.TRM.UpdateFailedPlanToDB(ctx, record.Uuid)
				ctxTx := boot.Begin(ctx)
				if subtask.Status != model.TaskStatusInTheTransit && (errors.Is(err, codes.ErrGlobalTransmitPathNotFound) || errors.Is(err, codes.ErrLocalTransmitPathNotFound)) &&
					(subtask.SubStatus != model.WalkAvoidFinish && subtask.SubStatus != model.WalkTempFinish) {
					//m.mc.TM.UpdateSubtaskStatusToDB(ctxTx, subtask.Id, model.TaskStatusInTheTransit, false)
					if err := m.mc.TM.UpdateSubtaskStatusToDB(ctxTx, subtask.Id, model.TaskStatusInTheTransit, false); err != nil {
						log.Errorf("update TaskStatusInTheTransit error:%s", err.Error())
						ctxTx.Rollback()
					}
				}
				ctxTx.Commit()
				continue
			}
			log.Infof("robot:%s retry task:%d plan success", record.AgvID, subtask.Id)
			m.mc.TRM.DeleteFailedPlanFromDB(ctx, record.Uuid, robot)
			tools.Sleep(1)
			//已删除失败任务
		}
		tools.Sleep(1)
	}
}
func (m *DispatchManager) processMapFailedPlanAvoid(ctx context.Context, mapName string) {
	for {
		records, errGet := m.mc.TRM.GetMapFailedPlanToDB(context.Background(), mapName)
		if errGet != nil {
			tools.Sleep(1)
			continue
		}
		var wg sync.WaitGroup
		for _, record := range records {
			robot := robotmanager.GetFromCache(record.AgvID)
			subtask, errGetTask := m.mc.TM.GetRobotStatusSubtaskFromDB(ctx, record.AgvID, []model.TaskStatus{model.TaskStatusRunning, model.TaskStatusInTheTransit, model.TaskStasusAvoid, model.TaskStatusPaused, model.TaskStatusContinuing})
			if errGetTask != nil {
				continue
			}
			if !robot.StatusIsExists() || robot.Status.Online == false || robot.Status.TaskOnline == false || robot.Status.Status == model.RobotStatusEmergenceStop {
				continue
			}
			//任务优先级相同且互相堵塞，判断堵塞AGV为避让中，则当前AGV不触发避让
			avoidFlag := m.SystemConfig.TrafficCfg.UseTaskAvoid //true
			if subtask.BlockAgv != "" && subtask.Grade == subtask.BlockTaskGrade {
				runTask, _ := m.mc.TM.GetRunningSubtaskFromDB(ctx, subtask.BlockAgv)
				blockPath, getErr := m.mc.TRM.GetRobotLocalPathFromDB(ctx, subtask.BlockAgv)
				if getErr == nil {
					if runTask != nil && runTask.Status == model.TaskStasusAvoid && len(blockPath.Paths) > 0 {
						log.Infof("任务优先级相同，robot%s等待，robot%s避让", subtask.AgvId, subtask.BlockAgv)
						avoidFlag = false
					}
				}
			}
			if m.TRM().IsAgvInFailedPlanCycleWait(ctx, subtask.BlockAgv) {
				avoidFlag = false
			}
			if subtask.AvoidSite == "" && avoidFlag {
				wg.Add(1)
				go m.handleTaskAvoidPoint(context.Background(), subtask, robot, &wg)
			}
			tools.Sleep(1)
		}
		wg.Wait()
		tools.Sleep(1)
	}
}

//失败任务处理
func (m *DispatchManager) processFailedPlan() {
	for _, mapName := range m.TRM().GetAllMapNames() {
		go m.processMapFailedPlan(context.Background(), mapName)
		go m.processMapFailedPlanAvoid(context.Background(), mapName)
	}
}
func (m *DispatchManager) MakeIdleAgvTask(mapName string) {
	robotInReject := func(robot *model.Robot, rejects map[string]string) bool {
		for _, site := range robot.AgvCorverNodes {
			if _, find := rejects[site]; find {
				return true
			}
		}
		return false
	}
	addIdleTask := func(agvId, target, finalTarget, blockAgv string, avoidType bool, blockPath model.TrafficPaths) {
		curRobot := robotmanager.GetFromCache(agvId)
		if curRobot.StatusIsExists() && curRobot.Status.SiteIdS == finalTarget {
			return
		}
		goalRejects := make(map[string]string, 0)
		pathRejects := make(map[string]string, 0)
		gPaths := make(model.TrafficPaths, 0)
		if curRobot.StatusIsExists() && curRobot.Account.MapName != "" {
			globalRejects := make(map[string]string, 0)

			if avoidType {
				_, _, globalRejects, _ = m.mc.MakePlanRejectNodes1(curRobot.Account.AgvId, curRobot.Account.MapName, curRobot.AgvCorverNodes, "")
			} else {
				globalRejects, _, _ = m.mc.MakePlanRejectNodes(curRobot.Account.AgvId, curRobot.Account.MapName, curRobot.AgvCorverNodes, nil)
			}
			targetTemp := target
			if curRobot.Status.SiteIdS == target {
				targetTemp = finalTarget
			}
			pathRejects[targetTemp] = targetTemp
			temp_node := m.mc.TRM.GetNode(mapName, targetTemp)
			for _, ban := range temp_node.BanNodes {
				if ban != "0" {
					pathRejects[ban] = ban
				}
			}
			if temp_node.Type == model.NodeStation && curRobot.Type.CategoryType == model.CategoryTypeThreeWay && temp_node.StockFrontId != "" {
				targetTemp = temp_node.StockFrontId
			}
			TmpGoalRejects, TmpPathRejects, TmpgPaths := m.mc.TRM.GetAgvBestGlobalPath(context.Background(), curRobot, curRobot.Account.MapName, curRobot.Status.SiteIdS, targetTemp, globalRejects, blockPath)
			if config.Conf.UseAllPath {
				for _, reject := range TmpGoalRejects {
					pathRejects[reject] = reject
				}
			} else {
				for _, reject := range TmpPathRejects {
					pathRejects[reject] = reject
				}
			}
			goalRejects = TmpGoalRejects
			gPaths = TmpgPaths
		}
		//新增
		if blockAgv != "" {
			blockRobot := robotmanager.GetFromCache(blockAgv)
			if blockRobot.StatusIsExists() && blockRobot.Status.SiteIdS != "" && blockRobot.Status.SiteIdS != "0" {
				pathRejects[blockRobot.Status.SiteIdS] = blockRobot.Status.SiteIdS
			}
		}

		robots := robotmanager.GetAllRobotsInSameMapFromCache(mapName)

		for _, robot := range robots {
			if robot.Account.AgvId == agvId {
				continue
			}
			robot = robotmanager.GetFromCache(robot.Account.AgvId) //每次计算从缓存中获取最新数据
			if robot.StatusIsExists() && robot.TaskOnline && robot.Status.ControlType == 1 && (robot.Status.Status == model.RobotStatusIdle || robot.Status.Status == model.RobotStatusStandby) {
				curNode := m.mc.TRM.GetNode(robot.Status.MapName, robot.Status.SiteIdS)
				if curNode.Type == model.NodeElevator || curNode.Type == model.NodeSwitch {
					continue
				}
				robotTask, _ := m.mc.TM.GetRobotRunTaskFromDB(context.Background(), robot.Account.AgvId)
				avoidTask, _ := m.mc.TM.GetRobotIdleAvoidTaskFromDB(context.Background(), robot.Account.AgvId)

				if robotTask == nil && avoidTask == nil {
					if robotInReject(robot, pathRejects) {
						mainTask, _ := m.mc.TM.GetMainTaskByAgvFromDB(context.Background(), robot.Account.AgvId)
						if mainTask != nil {
							if wt, _ := m.TM().GetFirstWaitingTaskByMainTaskCodeFromDB(context.Background(), mainTask.MainTaskCode); wt != nil && wt.RetryCnt < 2 {
								continue
							}
						}
						// globalRejects, globalRejectsAll, localRejects := mc.MakePlanRejectNodes(robot.Account.AgvId, robot.Account.MapName, robot.AgvCorverNodes)
						globalRejects, globalRejectsAll, localRejects, freeAvoidRejects := m.mc.MakePlanRejectNodes1(robot.Account.AgvId, robot.Account.MapName, robot.AgvCorverNodes, robot.Status.SiteIdS)
						siteId := m.mc.TRM.GetIdleAgvNearAvoid(context.Background(), robot, robots, curRobot.Status.SiteIdS, globalRejects, globalRejectsAll, localRejects, goalRejects, freeAvoidRejects, gPaths)
						if siteId.Id != "" {
							log.Warnf("空闲机器人robot%s 到站点%s避让-占用目标点%s（触发robot:%s）", robot.Account.AgvId, siteId.Id, robot.Status.SiteIdS, agvId)
							status := model.TaskStatusRegister
							var mainTaskCode string
							//重新获取当前最新的主任务信息
							mainTask, _ = m.mc.TM.GetMainTaskByAgvFromDB(context.Background(), robot.Account.AgvId)
							if mainTask != nil {
								mainTaskCode = mainTask.MainTaskCode
								status = model.TaskStatusAssign
							}

							m.mc.TM.AddToDB(context.Background(), &model.Task{
								Type:         model.TaskTypeAvoid,
								Status:       status,
								MapName:      robot.Account.MapName,
								AgvId:        robot.Account.AgvId,
								TargetSiteID: siteId.Id,
								MainTaskCode: mainTaskCode,
								CreatedTime:  time.Now(),
								AvoidAgv:     agvId,
								Grade:        2,
								GroupList:    []string{robot.Account.GroupID},
							})
						}
					}
				}
			}
		}
	}
	for {
		if m.SystemConfig.TrafficCfg.UseIdleAvoid {
			tasks, errGetTasks := m.mc.TM.GetAllStatusTasksByMapNameFromDB(context.Background(), model.TaskAssignStatus(), mapName)
			if errGetTasks != nil {
				log.Panicf("get all running tasks error:%s", errGetTasks.UnWrapError())
			}
			for _, t := range tasks {
				if t.AgvId == "" {
					continue
				}
				if t.Status == model.TaskStatusFinish || t.Status == model.TaskStatusInFinish {
					continue
				}
				if t.MainTaskCode != "" {
					if taskNew, err := m.mc.TM.GetFirstWaitingTaskByMainTaskCodeFromDB(context.Background(), t.MainTaskCode); err == nil && taskNew.Id != t.Id {
						continue
					}
				}
				curRobot := robotmanager.GetFromCache(t.AgvId)
				if !curRobot.StatusIsExists() || !curRobot.RobotIsUse() || curRobot.Status.MapName != mapName {
					continue
				}
				subtask, _ := m.mc.TM.GetRunningSubtaskFromDB(context.Background(), t.AgvId)
				if subtask != nil && subtask.MapName == mapName {
					if t.Status == model.TaskStasusAvoid {
						if subtask.SubStatus == model.WalkToTargetReplace || subtask.SubStatus == model.WalkToTempReplace || subtask.SubStatus == model.WalkForwardAvoid || subtask.SubStatus == model.WalkBackforwardAvoid || subtask.SubStatus == model.WalkTempFinish {
							addIdleTask(subtask.AgvId, subtask.TempTargetSite, subtask.TargetSiteID, subtask.BlockAgv, subtask.Type == model.TaskTypeAvoid, subtask.Paths)
							continue
						} else if (subtask.SubStatus == model.WalkNearAvoid || subtask.SubStatus == model.WalkAvoidFinish) && subtask.AvoidSite != "" {
							addIdleTask(subtask.AgvId, subtask.AvoidSite, subtask.TargetSiteID, subtask.BlockAgv, subtask.Type == model.TaskTypeAvoid, subtask.Paths)
							continue
						}
					}
					addIdleTask(subtask.AgvId, subtask.TargetSiteID, subtask.TargetSiteID, subtask.BlockAgv, subtask.Type == model.TaskTypeAvoid, subtask.Paths) //正在执行任务的站点不发送任务（全局路径无法规划）
				}
				subtask, _ = m.mc.TM.GetWaitingSubtaskFromDB(context.Background(), t.AgvId)
				if subtask != nil {
					addIdleTask(subtask.AgvId, subtask.TargetSiteID, subtask.TargetSiteID, subtask.BlockAgv, subtask.Type == model.TaskTypeAvoid, subtask.Paths)
				} else {
					if curRobot.Status.MapName == t.MapName {
						addIdleTask(t.AgvId, t.TargetSiteID, t.TargetSiteID, "", t.Type == model.TaskTypeAvoid, nil) //正在执行任务的站点不发送任务（全局路径无法规划）
					}
				}
			}
		}
		tools.Sleep(2)
	}

}

//空闲避让任务处理
func (m *DispatchManager) processIdleTask() {
	for _, mapName := range m.TRM().GetAllMapNames() {
		go m.MakeIdleAgvTask(mapName)
	}
}

//autoCleanDB 数据库自动清理
func (m *DispatchManager) startAutoCleanDB(days int) {
	// //任务表
	// m.mc.TM.StartAutoCleanDB(days)
	//日志表
	m.mc.LM.StartAutoCleanDB(days)
}

func (m *DispatchManager) startReceiveRMSErrCode() {
	for l := range boot.LogChan {
		errAdd := m.mc.LM.AddRMSLogToDB(context.TODO(), &model.RMSLog{
			Level:     l.Level,
			LevelStr:  l.Level.CapitalString(),
			Msg:       l.Message,
			StartTime: l.Time.Local().Format("2006-01-02 15:04:05.000000"),
		})
		if errAdd != nil {
			log.Errorf("add rms log error:%s", errAdd.Error())
		}
	}
}

//用于检测小车上线，进行任务重发
func (m *DispatchManager) RobotOnlineTaskAutoRetry(robot *model.Robot) {
	//获取所有小车，用于检测小车状态，进行任务重发
	ctx := context.Background()

	//获取小车执行中的任务
	subtask, errGetTask := m.mc.TM.GetRunningSubtaskFromDB(ctx, robot.Account.AgvId)
	if errGetTask != nil && !errors.Is(errGetTask, codes.ErrTaskNotExists) {
		log.Errorf("robot:%s 检测小车上线，任务重发失败，获取任务失败:%s", robot.Account.AgvId, errGetTask.Error())

		// err := m.mc.TRM.AddFailedPlanToDB(ctx, robot.Account.AgvId, model.AgvBreakTask)
		// if err != nil {
		// 	log.Errorf("robot:%s 检测小车上线，任务重发失败，把重发任务写入到重发列表失败:%s", robot.Account.AgvId, err.Error())
		// }
		return
	}
	if subtask == nil {
		return
	}
	if !subtask.Status.IsTaskRunningTaskForAutoRetryStatus() {
		return
	}
	if subtask.SubStatus != model.WalkAvoidFinish && subtask.SubStatus != model.WalkTempFinish {
		m.mc.TM.UpdateSubtaskKeyToDB(ctx, subtask.Id, "sub_status", model.InitState)
	}

	log.Infof("robot:%s 检测小车上线，开始进行把重发任务写入到重发列表 task:%d ", robot.Account.AgvId, subtask.Id)
	err := m.mc.TRM.AddFailedPlanToDB(ctx, robot.Account.AgvId, "", robot.Account.MapName, model.AgvBreakTask)
	if err != nil {
		log.Errorf("robot:%s 检测小车上线，把重发任务写入到重发列表失败:%s", robot.Account.AgvId, err.Error())
		return
	}
	log.Infof("robot:%s 检测小车上线，把重发任务写入到重发列表成功 task:%d ", robot.Account.AgvId, subtask.Id)
}

//根据类型向小车发送配置
func (m *DispatchManager) SendConfigToRobots(ctx context.Context, robotType *model.RobotType) {
	//下发到小车
	robots := robotmanager.GetAllFromCache()
	if robots != nil && len(robots) > 0 {
		for _, robot := range robots {
			if robot.Type.CategoryId == robotType.CategoryId && robot.Status != nil && robot.Status.Online {
				caller, _ := m.GetCaller(robot.Account.AgvId)
				if caller != nil {
					caller.SendConfig(ctx, robotType)
				}
			}
		}
	}
}

//根据类型向小车发送软件急停
func (m *DispatchManager) SendSoftStopToRobots(ctx context.Context, isStop bool) {
	//下发到小车
	robots := robotmanager.GetAllFromCache()
	if robots != nil && len(robots) > 0 {
		for _, robot := range robots {
			if robot.StatusIsExists() && robot.Status.Online {
				caller, _ := m.GetCaller(robot.Account.AgvId)
				if caller != nil {
					caller.SoftStop(ctx, isStop)

				}
			}
		}
	}
}
